/* 
 * Copyright 2023 Sven dj.Zheng All Rights Reserved. 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"). 
 * You may not use this file except in compliance with the License. 
 * A copy of the License is located at 
 * 
 *     http://aws.amazon.com/apache2.0/ 
 * 
 * or in the "license" file accompanying this file. This file is distributed 
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
 * express or implied. See the License for the specific language governing 
 * permissions and limitations under the License. 
 */

#ifndef __ULC_SDK_JSONUTILS_H_
#define __ULC_SDK_JSONUTILS_H_

#include "cJSON.h"

#include <memory>
#include <string>
//#include "Utils/Logging/LoggerUtils.h"
//#include <Utils/Logging/Logger.h>

namespace sdk {
namespace utils {
namespace cjson {

/**
 * Returns the tag for logging purposes.
 */
inline static std::string getTag() {
    return "JsonUtils";
}

/**
 * Invoke a cJSON parse on a JSON string.
 *
 * @param jsonContent The JSON content to be parsed.
 * @param[out] document The output parameter cJSON document.
 * @return @c true If the JSON content was valid, @c false otherwise.
 */
bool parseJSON(const std::string& jsonContent, cJSON** document);

/**
 * Converts a given rapidjson document node to a string. The node must be either of Object or String type.
 *
 * @param documentNode A logical node within a parsed JSON document which rapidjson understands.
 * @param[out] value The output parameter which will be assigned the string value.
 * @return @c true If the node was converted to a string ok, @c false otherwise.
 */
bool convertToValue(const cJSON *documentNode, std::string* value);

/**
 * Converts a given cJSON value node to a 64-bit signed integer. The node must be Int64 type.
 *
 * @param documentNode A logical node within a parsed JSON document which cJSON understands.
 * @param[out] value The output parameter which will be assigned the int64_t value.
 * @return @c true If the node was successfully converted, @c false otherwise.
 */
bool convertToValue(const cJSON *documentNode, int64_t* value);

/**
 * A template function to find and retrieve a value of type T from a direct child of the
 * provided @c cJSON object. The type T must have an overload of the function
 * @c convertToValue.
 *
 * @param jsonNode A logical node within a parsed JSON document which 'cJSON' understands.
 * @param key The key in which to look for the value.
 * @param[out] value The output parameter which will be assigned the value of type T if the function
 * succeeds. No modification is done in case of failure.
 * @return @c true If the value was successfully retrieved, @c false otherwise.
 */
template <typename T>
bool retrieveValue(cJSON *jsonNode, const std::string& key, T* value) {
    if (!value) {
        //logging::acsdkError(logging::LogEntry(getTag(), "retrieveValueFailed").d("reason", "nullValue"));
        return false;
    }

    cJSON *iterator = cJSON_GetObjectItem(jsonNode, key.c_str());
    if(!iterator) {
      return false;
    }
    
    return convertToValue(iterator, value);
}

/**
 * A template function to find and retrieve a value of type T from the provided JSON string.
 * The provided string will first be parsed into a JSON document, after which the associated
 * value T will be retrieved. The type T must have an overload of the function @c convertToValue.
 *
 * @param jsonString A JSON string.
 * @param key The key in which to look for the value.
 * @param[out] value The output parameter which will be assigned the value of type T if the function
 * succeeds. No modification is done in case of failure.
 * @return @c true If the value was successfully retrieved, @c false otherwise.
 */
template <typename T>
bool retrieveValue(const std::string jsonString, const std::string& key, T* value) {
    bool result = true;
    if (!value) {
        //logging::acsdkError(logging::LogEntry(getTag(), "retrieveValueFailed").d("reason", "nullValue"));
        return false;
    }

    cJSON *document = NULL;
  
    if (!parseJSON(jsonString, &document)) {
        //logging::acsdkError(logging::LogEntry(getTag(), "retrieveValueFailed").d("reason", "parsingError"));
        return false;
    }

    result = retrieveValue(document, key, value);
    if(document) {
      cJSON_Delete(document);
      document = NULL;
    }
    
    return result;
}

}  // namespace cjson
}  // namespace utils
}  // namespace sdk

#endif  // __ULC_SDK_JSONUTILS_H_